﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;

using Styx;
using Styx.Common;
using Styx.CommonBot;
using Styx.CommonBot.Profiles;
using Styx.Pathing;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using Styx.Plugins;

namespace rndbuddy
{
    static class ProfileHandler
    {
        static string profile_path = settings.conf_path;
        static string profile_name = "rndbuddy_profile_" + Crypto.GetHashString(settings.Me.Name) + ".xml";
        static string pb_core_profile = "rndbuddy_professionbuddy_" + Crypto.GetHashString(settings.Me.Name) + ".xml";

        public static string[] last_node = new string[] { "999", "999" };
        public static double distance_to_end = 9999;

        public static string current_profile = "empty";
        public static string profile_zone;
        public static double nph;
        public static double time_elapsed;

        public static int total_refresh = 0;
        public static DateTime profile_start;

        public static string pb_desired_zone = "870";

        public static bool get_profile()
        {
            if (!settings.valid_key)
                return false;

            rndbuddy.t_profile.Stop();

            rndbuddy.log("Beginning update sequence");
      
            //report latest statistics if profile is/was present
            if (!String.IsNullOrEmpty(ProfileHandler.current_profile))
            {
                ProfileHandler.determine_nph();
                server_pipe.report_profile_nph();

                rndbuddy.log("Reporting Crowdsourcer data to server");
                server_pipe.report_crowdsourcer_data(true);
            }

            //generate a new profile
            string[] response = server_pipe.request_new_profile();
            if (response[0] != "true")
            {
                rndbuddy.log("Error while fetching new Profile. Error = " + response[1]);
                return false;
            }

            //display info
            rndbuddy.log("New Profile = " + response[1]);
            rndbuddy.log("Destination Zone = " + response[2]);
            rndbuddy.logsilent("Setting Last Hotspot = " + response[3] + ";" + response[4]);

            //store profile info
            current_profile = response[1];
            profile_zone = response[2];

            //store last hotspot
            last_node[0] = response[3];
            last_node[1] = response[4];

            //calc first distance
            rndbuddy._toon.poll();
            distance_to_end = movement_watcher.calc_dist(rndbuddy._toon.x, rndbuddy._toon.y, response[3], response[4]);

            //download xml profile
            rndbuddy.log("Downloading Profile");

            string profile = server_pipe.download_profile(response[1]);
            if (profile.StartsWith("false"))
            {
                string[] msg = profile.Split(';');
                rndbuddy.log("Unable to fetch profile. Error = " + msg[1]);
                return false;
            }

            //save to file
            store_string_to_profile(profile);

            //reparse remote server setting
            settings.parse_remote_settings();

            //(re)load profile
            reload_profile();
            
            rndbuddy.t_profile.Start();
            
            ++total_refresh;
            profile_start = System.DateTime.Now;

            return true;

        }

        static public bool store_string_to_profile(string profile)
        {
            try
            {
                System.IO.File.WriteAllText(profile_path + profile_name, profile);

                //log profile content to logfile
                rndbuddy.logsilent("---DEBUG XML CONTENT ---");
                rndbuddy.logsilent(profile);
            }
            catch (Exception e)
            {
                rndbuddy.logcrit("Exception in Store Profile Method. Error given = " + e.Message);
                return false;
            }
            return true;
        }

        static public void reload_profile()
        {
            if (!File.Exists(profile_path+profile_name))
            {
                rndbuddy.logcrit("Unable to load Profile!");
                TreeRoot.Stop();
                return;
            }
            
            rndbuddy.log("Loading new Profile to Honorbuddy");
            
            //truncate profile
            ProfileManager.LoadEmpty();

            if (BotController.is_professionbuddy())
            {
                rndbuddy.log("Building ProfessionBuddy Core Profile");
                BuildPBCoreProfile();
                load_professionbuddy_farming_profile();

            }
            else if (BotController.is_gatherbuddy() || BotController.is_mixed_gb2())
            {
                ProfileManager.LoadNew(profile_path + profile_name);
            }
            else
            {
                throw new Exception("rndbuddy should serve profile reload request but no gatherbotbase was found");
            }

            return;
        }

        public static void determine_nph()
        {
            double nodes = 0;

            foreach (KeyValuePair<string, int> entry in Bots.Gatherbuddy.GatherbuddyBot.NodeCollectionCount)
                nodes += entry.Value;

            TimeSpan ts = Bots.Gatherbuddy.GatherbuddyBot.runningTime;
			
			//store to static member (needed by performance reporter)
			time_elapsed = ts.TotalSeconds;
			
            double _nph;
            _nph = nodes / ts.TotalHours;
            _nph = Math.Round(_nph, 3);
        
            //save
            nph = _nph;
        }

        public static void load_professionbuddy_ahbot()
        {
            rndbuddy.log("Loading ProfessionBuddy AHBot");
            string profile = Utilities.AssemblyDirectory + @"\Bots\Professionbuddy\Profiles\AHBot.xml";
            TreeAction("stop");
            LoadBotBase("ProfessionBuddy");
            ProfileManager.LoadNew(profile);
            TreeAction("start");
        }

        public static void LoadBotBase(string name)
        {
            rndbuddy.log("Setting Current Botbase to " + name);
            var Bot = BotManager.Instance.Bots.FirstOrDefault(kvp => kvp.Key == name);
            BotManager.Instance.SetCurrent(Bot.Value);
        }

        public static void BuildPBCoreProfile()
        {
            rndbuddy.log("Loading Professionbuddy");
            //string templatepath = settings.plugin_dir + "pbtemplate_" + pb_desired_zone + ".xml";
            string templatepath = settings.app_data_dir + "pbtemplate_" + pb_desired_zone + ".xml";
            if (System.IO.File.Exists(templatepath))
            {
                string pbtemplate = File.ReadAllText(templatepath);
                pbtemplate = pbtemplate.Replace(@"REPL:TOONNAME", profile_name);

                string[] s = server_pipe.get_toon_pb_settings();

                foreach (string t in s)
                {
                    if (String.IsNullOrEmpty(t))
                        continue;

                    string[] u = t.Split('=');
                    switch (u[0])
                    {
                        case "pb_smelt_ores":
                            rndbuddy.log("Professionbuddy Setting SMELT_ORES = " + num_to_bool(u[1]));
                            pbtemplate = pbtemplate.Replace(@"REPL:SETTINGS_SMELT_ORES", num_to_bool(u[1]));
                            break;
                        case "pb_use_ah":
                            rndbuddy.log("Professionbuddy Setting USE_AH = " + num_to_bool(u[1]));
                            pbtemplate = pbtemplate.Replace(@"REPL:SETTINGS_USE_AH", num_to_bool(u[1]));
                            break;
                        case "pb_use_gbank":
                            rndbuddy.log("Professionbuddy Setting USE_GBANK = " + num_to_bool(u[1]));
                            pbtemplate = pbtemplate.Replace(@"REPL:SETTINGS_USE_GBANK", num_to_bool(u[1]));
                            break;
                        case "pb_train_mining":
                            rndbuddy.log("Professionbuddy Setting TRAIN_MINING = " + num_to_bool(u[1]));
                            pbtemplate = pbtemplate.Replace(@"REPL:SETTINGS_TRAIN_MINING", num_to_bool(u[1]));
                            break;
                        default:
                            break;
                    }
                }

                TextWriter tw = new StreamWriter(settings.conf_path + pb_core_profile, false);
                tw.Write(pbtemplate);
                tw.Close();
            }
            else
            {
                rndbuddy.logcrit("File " + templatepath + " does not exist!");
                rndbuddy.logcrit("Make sure to start PB from a supported zone!");
                TreeRoot.Stop();
            }
        }

        public static void load_professionbuddy_farming_profile()
        {
            rndbuddy.log("Loading PB Farm Profile");
            string profile = settings.conf_path + pb_core_profile;
            if (!BotController.is_professionbuddy())
                LoadBotBase("ProfessionBuddy");
            try
            {
                ProfileManager.LoadNew(profile);
            }
            catch (Exception ex)
            {
                rndbuddy.logcrit("Unable to load Professionbuddy profile");
                rndbuddy.logcrit(ex.Message);
                rndbuddy.logcrit(ex.StackTrace);
                TreeRoot.Stop(ex.Message);
            }
        }

        //just testing... could be done so much easier - maybe add event reporting
        public static void TreeAction(string action)
        {
            switch (action)
            {
                case "start": TreeRoot.Start(); break;
                case "stop": TreeRoot.Stop(); break;
                default: break;
            }
        }

        public static void dyn_reload_check(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (!BotController.is_gather_botbase())
                return;

            ProfileHandler.distance_to_end = movement_watcher.calc_dist(StyxWoW.Me.X.ToString(), StyxWoW.Me.Y.ToString(), ProfileHandler.last_node[0], ProfileHandler.last_node[1]);

            if ((ProfileHandler.distance_to_end < settings.distance_to_end) && (settings.enable_refresh_on_end == 1))
            {
                rndbuddy.log("Reached end of profile, forcing reload");
                ProfileHandler.get_profile();
            }
        }

        public static void timed_reload(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (!BotController.is_gather_botbase())
                return;

            rndbuddy.log("Maximum Profile Time reached, refreshing");
            if (rndbuddy.t_profile.Enabled)
                ProfileHandler.get_profile();
                    
            //update timer interval
            try
            {
                rndbuddy.t_profile.Interval = Convert.ToDouble(settings.refresh_time) * 1000 * 60;
            }
            catch (Exception ex)
            {
                rndbuddy.logcrit("unable to update t_profile.Interval. "+ex.Message);
            }
        }

        public static string num_to_bool(string v)
        {
            switch (v)
            {
                case "1":
                    return "true";
                default:
                    return "false";
            }
        }

        public static string current_hb_profile_name()
        {
            var a = ProfileManager.CurrentProfile.Name;
            return a;
        }

        public static void calc_and_log_nph()
        {
            ProfileHandler.determine_nph();
            rndbuddy.log("Calced NPH = " + ProfileHandler.nph.ToString());
        }

        public static double calc_and_return_nph()
        {
            ProfileHandler.determine_nph();
            return Math.Round(ProfileHandler.nph, 2);
        }


    }
}
